/**
 * Copyright (C) 2021 - 2031 O-Cubes Co., Ltd.
 */

/****************************************************************
 *  @file    device_spi.c
 *  @brief   Designware spi controller device adapter layer
 *  @version v1.0
 *  @date    03. Apr. 2023
 *  @author  liuchao
 ****************************************************************/

#include "types.h"
#include "irq.h"
#include "mem_map_table.h"
#include "dw_spi.h"
#include "device_spi.h"
#include "crg.h"

/**
 * @name	DesignWare SPI 0 Object Instantiation
 * @{
 */
#if (USE_DW_SPI_0)
static void dw_spi_0_isr(void *ptr);
#define DW_SPI_0_REGBASE        (MEM_MAP_SPI0_BASE_ADDR)    /*!< designware spi 0 relative baseaddr */
#define DW_SPI_0_INTNO          (SPI0_IRQn)                 /*!< designware spi 0 interrupt number  */
#define DW_SPI_0_RX_SAMPLEDLY   0                           /*!< designware spi 0 RXD Sample Delay  */

#define DW_SPI_0_TX_FIFO_LEN    (32)
#define DW_SPI_0_RX_FIFO_LEN    (32)

#define DW_SPI_0_SUPPORTED_MODE DW_SPI_MASTER_SUPPORTED

DEV_SPI dw_spi_0;                                       /*!< designware spi object */
DW_SPI_CTRL dw_spi_0_ctrl;                              /*!< designware spi 0 ctrl */

/** designware spi 0 open */
static int32_t dw_spi_0_open(uint32_t mode, uint32_t param)
{
	return dw_spi_open(&dw_spi_0, mode, param);
}

/** designware spi 0 close */
static int32_t dw_spi_0_close(void)
{
	return dw_spi_close(&dw_spi_0);
}

/** designware spi 0 control */
static int32_t dw_spi_0_control(uint32_t ctrl_cmd, void *param)
{
	return dw_spi_control(&dw_spi_0, ctrl_cmd, param);
}

/** designware spi 0 write */
static int32_t dw_spi_0_write(uint8_t *w_buf, uint32_t w_len)
{
	return dw_spi_write(&dw_spi_0, w_buf, w_len);
}

/** designware spi 0 read */
static int32_t dw_spi_0_read(uint8_t *w_buf, uint32_t w_len, uint8_t *r_buf, uint32_t r_len)
{
	return dw_spi_write_read(&dw_spi_0, w_buf, w_len, r_buf, r_len);
}

/** designware spi 0 interrupt routine */
static void dw_spi_0_isr(void *ptr)
{
	dw_spi_isr(&dw_spi_0, ptr);
}

/** install designware spi 0 to system */
static void dw_spi_0_install(void)
{
	uint32_t spi_abs_base = 0;
	DEV_SPI *dw_spi_ptr = &dw_spi_0;
	DEV_SPI_INFO *dw_spi_info_ptr = &(dw_spi_0.spi_info);
	DW_SPI_CTRL *dw_spi_ctrl_ptr = &dw_spi_0_ctrl;
	DW_SPI_REG *dw_spi_reg_ptr;

	/**
	 * get absolute designware base address
	 */
	spi_abs_base = DW_SPI_0_REGBASE;

	dw_spi_reg_ptr = (DW_SPI_REG *)(long)spi_abs_base;

	/** spi info init */
	dw_spi_info_ptr->spi_ctrl = (void *)dw_spi_ctrl_ptr;
	dw_spi_info_ptr->status = DEV_DISABLED;
	dw_spi_info_ptr->freq = DW_SPI_DEFAULT_FREQ;
	dw_spi_info_ptr->opn_cnt = 0;
	dw_spi_info_ptr->mode = DEV_MASTER_MODE;
	dw_spi_info_ptr->clk_mode = SPI_CLK_MODE_DEFAULT;
	dw_spi_info_ptr->slave = SPI_SLAVE_NOT_SELECTED;
	dw_spi_info_ptr->dfs = SPI_DFS_DEFAULT;
	dw_spi_info_ptr->dummy = SPI_DUMMY_DEFAULT;

	/* spi ctrl init */
	/* Variables which should be set during object implementation */
	dw_spi_ctrl_ptr->dw_spi_regs = dw_spi_reg_ptr;
	dw_spi_ctrl_ptr->intno = DW_SPI_0_INTNO;
	dw_spi_ctrl_ptr->support_modes = DW_SPI_0_SUPPORTED_MODE;
	dw_spi_ctrl_ptr->tx_fifo_len = DW_SPI_0_TX_FIFO_LEN;
	dw_spi_ctrl_ptr->rx_fifo_len = DW_SPI_0_RX_FIFO_LEN;
	dw_spi_ctrl_ptr->dw_apb_bus_freq = get_mod_clk_rate(CRG_MOD_SPI0);
	dw_spi_ctrl_ptr->rx_sampledly = DW_SPI_0_RX_SAMPLEDLY;
	dw_spi_ctrl_ptr->dw_spi_int_handler = dw_spi_0_isr;
	/* Variables which always change during spi operation */
	dw_spi_ctrl_ptr->int_status = 0;

	/** spi dev init */
	dw_spi_ptr->spi_open = dw_spi_0_open;
	dw_spi_ptr->spi_close = dw_spi_0_close;
	dw_spi_ptr->spi_control = dw_spi_0_control;
	dw_spi_ptr->spi_write = dw_spi_0_write;
	dw_spi_ptr->spi_read = dw_spi_0_read;
}
#endif /* USE_DW_SPI_0 */
/** @} end of name */

/**
 * @name	DesignWare SPI 1 Object Instantiation
 * @{
 */
#if (USE_DW_SPI_1)
static void dw_spi_1_isr(void *ptr);
#define DW_SPI_1_REGBASE        (MEM_MAP_SPI1_BASE_ADDR)             /*!< designware spi 1 relative baseaddr */
#define DW_SPI_1_INTNO          (SPI1_IRQn)                 /*!< designware spi 1 interrupt number  */
#define DW_SPI_1_RX_SAMPLEDLY   0                               /*!< designware spi 1 RXD Sample Delay  */

#define DW_SPI_1_TX_FIFO_LEN    (32)
#define DW_SPI_1_RX_FIFO_LEN    (32)

#define DW_SPI_1_SUPPORTED_MODE DW_SPI_MASTER_SUPPORTED

DEV_SPI dw_spi_1;                                       /*!< designware spi 1 object */
DW_SPI_CTRL dw_spi_1_ctrl;                              /*!< designware spi 1 ctrl */

/** designware spi 1 open */
static int32_t dw_spi_1_open(uint32_t mode, uint32_t param)
{
	return dw_spi_open(&dw_spi_1, mode, param);
}

/** designware spi 1 close */
static int32_t dw_spi_1_close(void)
{
	return dw_spi_close(&dw_spi_1);
}

/** designware spi 1 control */
static int32_t dw_spi_1_control(uint32_t ctrl_cmd, void *param)
{
	return dw_spi_control(&dw_spi_1, ctrl_cmd, param);
}

/** designware spi 1 write */
static int32_t dw_spi_1_write(uint8_t *w_buf, uint32_t w_len)
{
	return dw_spi_write(&dw_spi_1, w_buf, w_len);
}

/** designware spi 1 read */
static int32_t dw_spi_1_read(uint8_t *w_buf, uint32_t w_len, uint8_t *r_buf, uint32_t r_len)
{
	return dw_spi_write_read(&dw_spi_1, w_buf, w_len, r_buf, r_len);
}

/** designware spi 1 interrupt routine */
static void dw_spi_1_isr(void *ptr)
{
	dw_spi_isr(&dw_spi_1, ptr);
}

/** install designware spi 1 to system */
static void dw_spi_1_install(void)
{
	uint32_t spi_abs_base = 0;
	DEV_SPI *dw_spi_ptr = &dw_spi_1;
	DEV_SPI_INFO *dw_spi_info_ptr = &(dw_spi_1.spi_info);
	DW_SPI_CTRL *dw_spi_ctrl_ptr = &dw_spi_1_ctrl;
	DW_SPI_REG *dw_spi_reg_ptr;

	/**
	 * get absolute designware base address
	 */
	spi_abs_base = (uint32_t)DW_SPI_1_REGBASE;
	dw_spi_reg_ptr = (DW_SPI_REG *)(long)spi_abs_base;

	/** spi info init */
	dw_spi_info_ptr->spi_ctrl = (void *)dw_spi_ctrl_ptr;
	dw_spi_info_ptr->status = DEV_DISABLED;
	dw_spi_info_ptr->freq = DW_SPI_DEFAULT_FREQ;
	dw_spi_info_ptr->opn_cnt = 0;
	dw_spi_info_ptr->mode = DEV_MASTER_MODE;
	dw_spi_info_ptr->clk_mode = SPI_CLK_MODE_DEFAULT;
	dw_spi_info_ptr->slave = SPI_SLAVE_NOT_SELECTED;
	dw_spi_info_ptr->dfs = SPI_DFS_DEFAULT;
	dw_spi_info_ptr->dummy = SPI_DUMMY_DEFAULT;

	/* spi ctrl init */
	/* Variables which should be set during object implementation */
	dw_spi_ctrl_ptr->dw_spi_regs = dw_spi_reg_ptr;
	dw_spi_ctrl_ptr->intno = DW_SPI_1_INTNO;
	dw_spi_ctrl_ptr->support_modes = DW_SPI_1_SUPPORTED_MODE;
	dw_spi_ctrl_ptr->tx_fifo_len = DW_SPI_1_TX_FIFO_LEN;
	dw_spi_ctrl_ptr->rx_fifo_len = DW_SPI_1_RX_FIFO_LEN;
	dw_spi_ctrl_ptr->rx_sampledly = DW_SPI_1_RX_SAMPLEDLY;
	dw_spi_ctrl_ptr->dw_apb_bus_freq = get_mod_clk_rate(CRG_MOD_SPI1);
	dw_spi_ctrl_ptr->dw_spi_int_handler = dw_spi_1_isr;
	/* Variables which always change during spi operation */
	dw_spi_ctrl_ptr->int_status = 0;

	/** spi dev init */
	dw_spi_ptr->spi_open = dw_spi_1_open;
	dw_spi_ptr->spi_close = dw_spi_1_close;
	dw_spi_ptr->spi_control = dw_spi_1_control;
	dw_spi_ptr->spi_write = dw_spi_1_write;
	dw_spi_ptr->spi_read = dw_spi_1_read;
}
#endif /* USE_DW_SPI_1 */
/** @} end of name */

/**
 * @name	DesignWare SPI 2 Object Instantiation
 * @{
 */
#if (USE_DW_SPI_2)
static void dw_spi_2_isr(void *ptr);
#define DW_SPI_2_REGBASE        (MEM_MAP_SPI2_BASE_ADDR)             /*!< designware spi 2 relative baseaddr */
#define DW_SPI_2_INTNO          (SPI2_IRQn)                 /*!< designware spi 2 interrupt number  */
#define DW_SPI_2_RX_SAMPLEDLY   0                               /*!< designware spi 2 RXD Sample Delay  */

#define DW_SPI_2_TX_FIFO_LEN    (32)
#define DW_SPI_2_RX_FIFO_LEN    (32)

#define DW_SPI_2_SUPPORTED_MODE DW_SPI_BOTH_SUPPORTED

DEV_SPI dw_spi_2;                                       /*!< designware spi 2 object */
DW_SPI_CTRL dw_spi_2_ctrl;                              /*!< designware spi 2 ctrl */

/** designware spi 2 open */
static int32_t dw_spi_2_open(uint32_t mode, uint32_t param)
{
	return dw_spi_open(&dw_spi_2, mode, param);
}

/** designware spi 2 close */
static int32_t dw_spi_2_close(void)
{
	return dw_spi_close(&dw_spi_2);
}

/** designware spi 2 control */
static int32_t dw_spi_2_control(uint32_t ctrl_cmd, void *param)
{
	return dw_spi_control(&dw_spi_2, ctrl_cmd, param);
}

/** designware spi 2 write */
static int32_t dw_spi_2_write(uint8_t *w_buf, uint32_t w_len)
{
	return dw_spi_write(&dw_spi_2, w_buf, w_len);
}

/** designware spi 2 read */
static int32_t dw_spi_2_read(uint8_t *w_buf, uint32_t w_len, uint8_t *r_buf, uint32_t r_len)
{
	return dw_spi_write_read(&dw_spi_2, w_buf, w_len, r_buf, r_len);
}

/** designware spi 2 interrupt routine */
static void dw_spi_2_isr(void *ptr)
{
	dw_spi_isr(&dw_spi_2, ptr);
}

/** install designware spi 2 to system */
static void dw_spi_2_install(void)
{
	uint32_t spi_abs_base = 0;
	DEV_SPI *dw_spi_ptr = &dw_spi_2;
	DEV_SPI_INFO *dw_spi_info_ptr = &(dw_spi_2.spi_info);
	DW_SPI_CTRL *dw_spi_ctrl_ptr = &dw_spi_2_ctrl;
	DW_SPI_REG *dw_spi_reg_ptr;

	/**
	 * get absolute designware base address
	 */
	spi_abs_base = (uint32_t)DW_SPI_2_REGBASE;
	dw_spi_reg_ptr = (DW_SPI_REG *)(long)spi_abs_base;

	/** spi info init */
	dw_spi_info_ptr->spi_ctrl = (void *)dw_spi_ctrl_ptr;
	dw_spi_info_ptr->status = DEV_DISABLED;
	dw_spi_info_ptr->freq = DW_SPI_DEFAULT_FREQ;
	dw_spi_info_ptr->opn_cnt = 0;
	dw_spi_info_ptr->mode = DEV_MASTER_MODE;
	dw_spi_info_ptr->clk_mode = SPI_CLK_MODE_DEFAULT;
	dw_spi_info_ptr->slave = SPI_SLAVE_NOT_SELECTED;
	dw_spi_info_ptr->dfs = SPI_DFS_DEFAULT;
	dw_spi_info_ptr->dummy = SPI_DUMMY_DEFAULT;

	/* spi ctrl init */
	/* Variables which should be set during object implementation */
	dw_spi_ctrl_ptr->dw_spi_regs = dw_spi_reg_ptr;
	dw_spi_ctrl_ptr->intno = DW_SPI_2_INTNO;
	dw_spi_ctrl_ptr->support_modes = DW_SPI_2_SUPPORTED_MODE;
	dw_spi_ctrl_ptr->tx_fifo_len = DW_SPI_2_TX_FIFO_LEN;
	dw_spi_ctrl_ptr->rx_fifo_len = DW_SPI_2_RX_FIFO_LEN;
	dw_spi_ctrl_ptr->rx_sampledly = DW_SPI_2_RX_SAMPLEDLY;
	dw_spi_ctrl_ptr->dw_apb_bus_freq = get_mod_clk_rate(CRG_MOD_SPI2);
	dw_spi_ctrl_ptr->dw_spi_int_handler = dw_spi_2_isr;
	/* Variables which always change during spi operation */
	dw_spi_ctrl_ptr->int_status = 0;

	/** spi dev init */
	dw_spi_ptr->spi_open = dw_spi_2_open;
	dw_spi_ptr->spi_close = dw_spi_2_close;
	dw_spi_ptr->spi_control = dw_spi_2_control;
	dw_spi_ptr->spi_write = dw_spi_2_write;
	dw_spi_ptr->spi_read = dw_spi_2_read;
}
#endif /* USE_DW_SPI_2 */
/** @} end of name */

/**
 * @name	DesignWare SPI 2 Object Instantiation
 * @{
 */
#if (USE_DW_SPI_3)
static void dw_spi_3_isr(void *ptr);
#define DW_SPI_3_REGBASE        (MEM_MAP_SPI3_BASE_ADDR)             /*!< designware spi 2 relative baseaddr */
#define DW_SPI_3_INTNO          (SPI3_IRQn)                 /*!< designware spi 2 interrupt number  */
#define DW_SPI_3_RX_SAMPLEDLY   0                               /*!< designware spi 2 RXD Sample Delay  */

#define DW_SPI_3_TX_FIFO_LEN    (32)
#define DW_SPI_3_RX_FIFO_LEN    (32)

#define DW_SPI_3_SUPPORTED_MODE DW_SPI_MASTER_SUPPORTED

DEV_SPI dw_spi_3;                                       /*!< designware spi 2 object */
DW_SPI_CTRL dw_spi_3_ctrl;                              /*!< designware spi 2 ctrl */

/** designware spi 3 open */
static int32_t dw_spi_3_open(uint32_t mode, uint32_t param)
{
	return dw_spi_open(&dw_spi_3, mode, param);
}

/** designware spi 3 close */
static int32_t dw_spi_3_close(void)
{
	return dw_spi_close(&dw_spi_3);
}

/** designware spi 3 control */
static int32_t dw_spi_3_control(uint32_t ctrl_cmd, void *param)
{
	return dw_spi_control(&dw_spi_3, ctrl_cmd, param);
}

/** designware spi 3 write */
static int32_t dw_spi_3_write(uint8_t *w_buf, uint32_t w_len)
{
	return dw_spi_write(&dw_spi_3, w_buf, w_len);
}

/** designware spi 3 read */
static int32_t dw_spi_3_read(uint8_t *w_buf, uint32_t w_len, uint8_t *r_buf, uint32_t r_len)
{
	return dw_spi_write_read(&dw_spi_3, w_buf, w_len, r_buf, r_len);
}

/** designware spi 3 interrupt routine */
static void dw_spi_3_isr(void *ptr)
{
	dw_spi_isr(&dw_spi_3, ptr);
}

/** install designware spi 3 to system */
static void dw_spi_3_install(void)
{
	uint32_t spi_abs_base = 0;
	DEV_SPI *dw_spi_ptr = &dw_spi_3;
	DEV_SPI_INFO *dw_spi_info_ptr = &(dw_spi_3.spi_info);
	DW_SPI_CTRL *dw_spi_ctrl_ptr = &dw_spi_3_ctrl;
	DW_SPI_REG *dw_spi_reg_ptr;

	/**
	 * get absolute designware base address
	 */
	spi_abs_base = (uint32_t)DW_SPI_3_REGBASE;
	dw_spi_reg_ptr = (DW_SPI_REG *)(long)spi_abs_base;

	/** spi info init */
	dw_spi_info_ptr->spi_ctrl = (void *)dw_spi_ctrl_ptr;
	dw_spi_info_ptr->status = DEV_DISABLED;
	dw_spi_info_ptr->freq = DW_SPI_DEFAULT_FREQ;
	dw_spi_info_ptr->opn_cnt = 0;
	dw_spi_info_ptr->mode = DEV_MASTER_MODE;
	dw_spi_info_ptr->clk_mode = SPI_CLK_MODE_DEFAULT;
	dw_spi_info_ptr->slave = SPI_SLAVE_NOT_SELECTED;
	dw_spi_info_ptr->dfs = SPI_DFS_DEFAULT;
	dw_spi_info_ptr->dummy = SPI_DUMMY_DEFAULT;

	/* spi ctrl init */
	/* Variables which should be set during object implementation */
	dw_spi_ctrl_ptr->dw_spi_regs = dw_spi_reg_ptr;
	dw_spi_ctrl_ptr->intno = DW_SPI_3_INTNO;
	dw_spi_ctrl_ptr->support_modes = DW_SPI_3_SUPPORTED_MODE;
	dw_spi_ctrl_ptr->tx_fifo_len = DW_SPI_3_TX_FIFO_LEN;
	dw_spi_ctrl_ptr->rx_fifo_len = DW_SPI_3_RX_FIFO_LEN;
	dw_spi_ctrl_ptr->rx_sampledly = DW_SPI_3_RX_SAMPLEDLY;
	dw_spi_ctrl_ptr->dw_apb_bus_freq = get_mod_clk_rate(CRG_MOD_SPI3);
	dw_spi_ctrl_ptr->dw_spi_int_handler = dw_spi_3_isr;
	/* Variables which always change during spi operation */
	dw_spi_ctrl_ptr->int_status = 0;

	/** spi dev init */
	dw_spi_ptr->spi_open = dw_spi_3_open;
	dw_spi_ptr->spi_close = dw_spi_3_close;
	dw_spi_ptr->spi_control = dw_spi_3_control;
	dw_spi_ptr->spi_write = dw_spi_3_write;
	dw_spi_ptr->spi_read = dw_spi_3_read;
}
#endif /* USE_DW_SPI_2 */
/** @} end of name */

/**
 * @brief	install all spi objects
 * @note	@b MUST be called during system init
 */
void dw_spi_all_install(void)
{
#if (USE_DW_SPI_0)
	dw_spi_0_install();
#endif
#if (USE_DW_SPI_1)
	dw_spi_1_install();
#endif
#if (USE_DW_SPI_2)
	dw_spi_2_install();
#endif
#if (USE_DW_SPI_3)
	dw_spi_3_install();
#endif
}

/** get one designware device structure */
DEV_SPI_PTR spi_get_dev(int32_t spi_id)
{
	static uint32_t install_flag = 0;

	/* install device objects */
	if (install_flag == 0) {
		install_flag = 1;
		dw_spi_all_install();
	}

	switch (spi_id) {
#if (USE_DW_SPI_0)

		case DW_SPI_0_ID:
			return &dw_spi_0;
#endif
#if (USE_DW_SPI_1)

		case DW_SPI_1_ID:
			return &dw_spi_1;
#endif
#if (USE_DW_SPI_2)

		case DW_SPI_2_ID:
			return &dw_spi_2;
#endif
#if (USE_DW_SPI_3)

		case DW_SPI_3_ID:
			return &dw_spi_3;
#endif

		default:
			break;
	}

	return NULL;
}
